package com.gitee.huanminabc.jcommon.reflect;


import com.gitee.huanminabc.jcommon.exception.CommonException;
import com.gitee.huanminabc.jcommon.str.StringUtil;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

/**
 * 反射工具
 *
 * @Author: huanmin
 * @Date: 2022/6/19 21:49
 * @Version: 1.0
 * @Description: 文件作用详细描述....
 */
public final class MethodUtil {


    //获取指定名称的方法,如果当前类没有那么就去父类找
    public static Method getMethod(Class<?> cls, String methodName, Class<?>... parameterTypes) {

        //如果parameterTypes是null那么就需要把方法都查询出来进行匹配
        if (parameterTypes == null ) {
            for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
                Method[] declaredMethods = cls.getDeclaredMethods();
                for (Method method : declaredMethods) {
                    if (method.getName().equals(methodName)) {
                        if (!Modifier.isPublic(method.getModifiers())) {
                            method.setAccessible(true);
                        }
                        return method;
                    }
                }
            }
            //接口里面的默认方法
            Method match = null;
            for (final Class<?> class1 : ClassUtil.getAllInterfaces(cls)) {
                Method[] methods = class1.getMethods();
                for (Method method : methods) {
                    if (method.getName().equals(methodName)) {
                        match = method;
                        break;
                    }
                }
            }
            return match;
        }

        for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
            try {
                final Method method = acls.getDeclaredMethod(methodName,parameterTypes);
                if (!Modifier.isPublic(method.getModifiers())) {
                    method.setAccessible(true);
                }
                return method;
            } catch (final NoSuchMethodException ex) { // NOPMD
                // ignore
            }
        }
        //接口里面的默认方法
        Method match = null;
        for (final Class<?> class1 : ClassUtil.getAllInterfaces(cls)) {
            try {
                match = class1.getMethod(methodName,parameterTypes);
            } catch (final NoSuchMethodException ex) { // NOPMD
                // ignore
            }
        }
        return match;
    }



    //获取类的全部自有方法,不包括toString,hashCode,equals
    public static Method[] getAllPublicOneselfMethods(Class<?> clazz) {
        Method[] methods = clazz.getDeclaredMethods();
        return filterNormalPublicMethods(methods);
    }


    //获取类的全部自有方法包括父类的 ,不包括toString,hashCode,equals ,
    public static Method[] getAllPublicMethods(Class<?> clazz) {
        Method[] methods = clazz.getDeclaredMethods();
        Method[] allMethods;
        //父类的方法
        if (clazz.getSuperclass()!=null){
            Method[] superMethods = clazz.getSuperclass().getDeclaredMethods();
            allMethods= new Method[methods.length + superMethods.length];
            System.arraycopy(methods, 0, allMethods, 0, methods.length);
            System.arraycopy(superMethods, 0, allMethods, methods.length, superMethods.length);
        }else{
            allMethods = methods;
        }
        return filterNormalPublicMethods(allMethods);
    }

    public static Method[] filterNormalPublicMethods(Method[] methods) {
        return Arrays.stream(methods)
                .filter(method -> !isEqualsMethod(method) && !isHashCodeMethod(method) && !isToStringMethod(method))
                //必须是public方法
                .filter(method -> Modifier.isPublic(method.getModifiers()))
                //必须是非静态方法
                .filter(method -> !Modifier.isStatic(method.getModifiers()))
                //自己的不能是Object的方法
                .filter(method -> !method.getDeclaringClass().equals(Object.class))
                .toArray(Method[]::new);
    }

    //根据List<Class<?>> classes 返回指定注解的所有方法 List
    public static List<Method> getAnnotationMethod(List<Class<?>> classes, Class<? extends Annotation> annotation) {
        return classes.parallelStream()
                .flatMap(clazz -> {
                    Method[] methods = clazz.getDeclaredMethods();
                    return Arrays.stream(methods)
                            .filter(method -> method.isAnnotationPresent(annotation));
                })
                .collect(Collectors.toList());
    }

    //获取指定注解的所有方法
    public static Method[] getAnnotationMethod(Class<?> clazz, Class<? extends Annotation> annotation) {
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(annotation)) {
                return methods;
            }
        }
        return null;
    }

    /**
     * 调用无参数方法
     * @param method 方法对象
     * @param target 调用对象
     * @return 执行结果
     */
    public static Object runMethod( Object target,Method method) {
        return runMethod(target,method, new Object[0]);
    }
    //通过名称调用无惨方法
    public static Object runMethod(Class<?> clazz, String methodName) {
        try {
            Object o = clazz.newInstance();
            //通过方法名和 参数个数+类型 能确认调用具体的方式
            Method method = clazz.getMethod(methodName);
            ReflectUtil.setAccessible(method);
            // 找到具体方法然后给方法传入参数
            return method.invoke(o);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                 NoSuchMethodException e) {
            throw new CommonException(e);
        }
    }
    public static Object runMethod(Object o, String methodName) {
        try {
            //通过方法名和 参数个数+类型 能确认调用具体的方式
            Method method = o.getClass().getMethod(methodName);
            ReflectUtil.setAccessible(method);
            // 找到具体方法然后给方法传入参数
            return method.invoke(o, new Object[0]);
        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            throw new CommonException(e);
        }
    }

    /**
     * 调用指定对象的方法
     *
     * @param method 方法对象
     * @param target 调用对象
     * @param args   方法参数
     * @return 执行结果
     */
    public static Object runMethod( Object target, Method method,Object... args) {
        try {
            ReflectUtil.setAccessible(method);
            return method.invoke(target, args);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new CommonException(e);
        }
    }

    public static Object runMethod(Object target, String methodName, Class<?>[] parameterTypes, Object[] args) {
        try {
            //通过方法名和 参数个数+类型 能确认调用具体的方式
            Method method = target.getClass().getMethod(methodName, parameterTypes);
            ReflectUtil.setAccessible(method);
            // 找到具体方法然后给方法传入参数

            return method.invoke(target, args);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            throw new CommonException(e);
        }
    }

    //运行指定类的所有公共无惨方法
    public static void runMethodAll(Class<?> clazz) {
        try {
            Method[] methods = clazz.getDeclaredMethods();
            Object o = clazz.newInstance();
            for (Method method : methods) {
                //判断是否为无参方法
                if (method.getParameterCount() == 0) {
                    ReflectUtil.setAccessible(method);
                    method.invoke(o);
                }
            }
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new CommonException(e);
        }
    }


    /**
     * 是否为equals方法
     *
     * @see Object#equals(Object)
     */
    public static boolean isEqualsMethod(Method method) {
        if (!"equals".equals(method.getName())) {
            return false;
        }
        Class<?>[] paramTypes = method.getParameterTypes();
        return (paramTypes.length == 1 && paramTypes[0] == Object.class);
    }

    /**
     * 是否为hashCode方法
     *
     * @see Object#hashCode()
     */
    public static boolean isHashCodeMethod(Method method) {
        return "hashCode".equals(method.getName()) && method.getParameterCount() == 0;
    }

    /**
     * 是否为Object的toString方法
     *
     * @see Object#toString()
     */
    public static boolean isToStringMethod(Method method) {
        return "toString".equals(method.getName()) && method.getParameterCount() == 0;
    }





    /**
     * 获取字段get方法
     *
     * @param cls   class
     * @param field 字段
     * @return Get方法
     */
    public static Method getFrontMethod(Class<?> cls, Field field) {
        try {
            return cls.getDeclaredMethod(getMethodCapitalize(field));
        } catch (NoSuchMethodException e) {
            throw new CommonException("Error: NoSuchMethod in %s.  Cause:", e, cls.getName());
        }
    }

    /**
     * 反射 method 方法名，例如 getId   ,  isWeb
     *
     * @param field
     */
    public static String getMethodCapitalize(Field field) {
        Class<?> fieldType = field.getType();
        // fix #176
        return concatCapitalize(boolean.class.equals(fieldType) ? "is" : "get", field.getName());
    }

    private static String concatCapitalize(String concatStr, final String str) {
        if (StringUtil.isEmpty(concatStr)) {
            concatStr = "";
        }
        if (str == null || str.isEmpty()) {
            return str;
        }

        final char firstChar = str.charAt(0);
        if (Character.isTitleCase(firstChar)) {
            // already capitalized
            return str;
        }

        return concatStr + Character.toTitleCase(firstChar) + str.substring(1);
    }

    //通过属性名称获方法名称 ,get,set,is
    public static String fieldToGetMethod(String fieldName) {
        return "get" + fieldName.substring(0, 1).toUpperCase(Locale.ENGLISH) + fieldName.substring(1);
    }

    public static String fieldToSetMethod(String fieldName) {
        return "set" + fieldName.substring(0, 1).toUpperCase(Locale.ENGLISH) + fieldName.substring(1);
    }

    public static String fieldToIsMethod(String fieldName) {
        return "is" + fieldName.substring(0, 1).toUpperCase(Locale.ENGLISH) + fieldName.substring(1);
    }
}
